arelle-release 2.37.44__py3-none-any.whl → 2.37.46__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.
- arelle/Cntlr.py +35 -0
- arelle/ErrorManager.py +306 -0
- arelle/ModelRelationshipSet.py +1 -1
- arelle/ModelXbrl.py +45 -233
- arelle/PluginManager.py +4 -0
- arelle/Validate.py +2 -0
- arelle/XbrlConst.py +1 -0
- arelle/_version.py +2 -2
- arelle/plugin/validate/EDINET/Constants.py +4 -4
- arelle/plugin/validate/EDINET/ControllerPluginData.py +80 -0
- arelle/plugin/validate/EDINET/{FormType.py → InstanceType.py} +8 -8
- arelle/plugin/validate/EDINET/{Manifest.py → ManifestInstance.py} +78 -33
- arelle/plugin/validate/EDINET/PluginValidationDataExtension.py +21 -10
- arelle/plugin/validate/EDINET/ValidationPluginExtension.py +20 -3
- arelle/plugin/validate/EDINET/__init__.py +4 -22
- arelle/plugin/validate/EDINET/rules/edinet.py +1 -1
- arelle/plugin/validate/EDINET/rules/manifests.py +88 -0
- arelle/plugin/validate/EDINET/rules/upload.py +51 -53
- arelle/utils/validate/ESEFImage.py +7 -7
- {arelle_release-2.37.44.dist-info → arelle_release-2.37.46.dist-info}/METADATA +1 -1
- {arelle_release-2.37.44.dist-info → arelle_release-2.37.46.dist-info}/RECORD +25 -22
- {arelle_release-2.37.44.dist-info → arelle_release-2.37.46.dist-info}/WHEEL +0 -0
- {arelle_release-2.37.44.dist-info → arelle_release-2.37.46.dist-info}/entry_points.txt +0 -0
- {arelle_release-2.37.44.dist-info → arelle_release-2.37.46.dist-info}/licenses/LICENSE.md +0 -0
- {arelle_release-2.37.44.dist-info → arelle_release-2.37.46.dist-info}/top_level.txt +0 -0
arelle/Cntlr.py
CHANGED
|
@@ -24,6 +24,8 @@ import regex as re
|
|
|
24
24
|
|
|
25
25
|
from arelle import Locale, ModelManager, PackageManager, PluginManager, XbrlConst
|
|
26
26
|
from arelle.BetaFeatures import BETA_FEATURES_AND_DESCRIPTIONS
|
|
27
|
+
from arelle.ErrorManager import ErrorManager
|
|
28
|
+
from arelle.FileSource import FileSource
|
|
27
29
|
from arelle.SystemInfo import PlatformOS, getSystemWordSize, hasFileSystem, hasWebServer, isCGI, isGAE
|
|
28
30
|
from arelle.WebCache import WebCache
|
|
29
31
|
from arelle.logging.formatters.LogFormatter import LogFormatter, logRefsFileLines # noqa: F401 - for reimport
|
|
@@ -123,6 +125,7 @@ class Cntlr:
|
|
|
123
125
|
"""
|
|
124
126
|
__version__ = "1.6.0"
|
|
125
127
|
betaFeatures: dict[str, bool]
|
|
128
|
+
errorManager: ErrorManager | None
|
|
126
129
|
hasWin32gui: bool
|
|
127
130
|
hasGui: bool
|
|
128
131
|
hasFileSystem: bool
|
|
@@ -164,6 +167,7 @@ class Cntlr:
|
|
|
164
167
|
b: betaFeatures.get(b, False)
|
|
165
168
|
for b in BETA_FEATURES_AND_DESCRIPTIONS.keys()
|
|
166
169
|
}
|
|
170
|
+
self.errorManager = None
|
|
167
171
|
self.hasWin32gui = False
|
|
168
172
|
self.hasGui = hasGui
|
|
169
173
|
self.hasFileSystem = hasFileSystem() # no file system on Google App Engine servers
|
|
@@ -291,6 +295,32 @@ class Cntlr:
|
|
|
291
295
|
PackageManager.init(self, loadPackagesConfig=hasGui)
|
|
292
296
|
|
|
293
297
|
self.startLogging(logFileName, logFileMode, logFileEncoding, logFormat)
|
|
298
|
+
self.errorManager = ErrorManager(self.modelManager, logging._checkLevel("INCONSISTENCY")) # type: ignore[attr-defined]
|
|
299
|
+
|
|
300
|
+
def error(self, codes: Any, msg: str, level: str = "ERROR", fileSource: FileSource | None = None, **args: Any) -> None:
|
|
301
|
+
if self.logger is None or self.errorManager is None:
|
|
302
|
+
self.addToLog(
|
|
303
|
+
message=msg,
|
|
304
|
+
messageCode=str(codes),
|
|
305
|
+
messageArgs=args,
|
|
306
|
+
level=level
|
|
307
|
+
)
|
|
308
|
+
return
|
|
309
|
+
self.errorManager.log(
|
|
310
|
+
self.logger,
|
|
311
|
+
level,
|
|
312
|
+
codes,
|
|
313
|
+
msg,
|
|
314
|
+
fileSource=fileSource,
|
|
315
|
+
logRefObjectProperties=getattr(self.logger, "logRefObjectProperties", False),
|
|
316
|
+
**args
|
|
317
|
+
)
|
|
318
|
+
|
|
319
|
+
@property
|
|
320
|
+
def errors(self) -> list[str | None]:
|
|
321
|
+
if self.errorManager is None:
|
|
322
|
+
return []
|
|
323
|
+
return self.errorManager.errors
|
|
294
324
|
|
|
295
325
|
@property
|
|
296
326
|
def uiLangDir(self) -> str:
|
|
@@ -662,3 +692,8 @@ class Cntlr:
|
|
|
662
692
|
|
|
663
693
|
def _clearPluginData(self) -> None:
|
|
664
694
|
self.__pluginData.clear()
|
|
695
|
+
|
|
696
|
+
def testcaseVariationReset(self) -> None:
|
|
697
|
+
self._clearPluginData()
|
|
698
|
+
if self.errorManager is not None:
|
|
699
|
+
self.errorManager.clear()
|
arelle/ErrorManager.py
ADDED
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
"""
|
|
2
|
+
See COPYRIGHT.md for copyright information.
|
|
3
|
+
"""
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
import logging
|
|
7
|
+
import os
|
|
8
|
+
from collections import defaultdict
|
|
9
|
+
from decimal import Decimal
|
|
10
|
+
from typing import TYPE_CHECKING, Any, Union, cast
|
|
11
|
+
|
|
12
|
+
from arelle import UrlUtil, XmlUtil, ModelValue, XbrlConst
|
|
13
|
+
from arelle.FileSource import FileSource
|
|
14
|
+
from arelle.Locale import format_string
|
|
15
|
+
from arelle.ModelObject import ModelObject, ObjectPropertyViewWrapper
|
|
16
|
+
from arelle.PluginManager import hasPluginWithHook, pluginClassMethods
|
|
17
|
+
from arelle.PythonUtil import flattenSequence
|
|
18
|
+
|
|
19
|
+
if TYPE_CHECKING:
|
|
20
|
+
from arelle.ModelManager import ModelManager
|
|
21
|
+
from arelle.ModelXbrl import ModelXbrl
|
|
22
|
+
from arelle.typing import EmptyTuple
|
|
23
|
+
|
|
24
|
+
LoggableValue = Union[str, dict[Any, Any], list[Any], set[Any], tuple[Any, ...]]
|
|
25
|
+
EMPTY_TUPLE: EmptyTuple = ()
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class ErrorManager:
|
|
29
|
+
logHasRelevelerPlugin: bool | None
|
|
30
|
+
_errorCaptureLevel: int
|
|
31
|
+
_errors: list[str | None]
|
|
32
|
+
_logCount: dict[str, int] = {}
|
|
33
|
+
_logRefFileRelUris: defaultdict[Any, dict[str, str]]
|
|
34
|
+
_modelManager: ModelManager
|
|
35
|
+
|
|
36
|
+
def __init__(self, modelManager: ModelManager, errorCaptureLevel: int):
|
|
37
|
+
self._errorCaptureLevel = errorCaptureLevel
|
|
38
|
+
self._errors = []
|
|
39
|
+
self._logCount = {}
|
|
40
|
+
self._logRefFileRelUris = defaultdict(dict)
|
|
41
|
+
self._modelManager = modelManager
|
|
42
|
+
self.logHasRelevelerPlugin = None
|
|
43
|
+
|
|
44
|
+
@property
|
|
45
|
+
def errors(self) -> list[str | None]:
|
|
46
|
+
return self._errors
|
|
47
|
+
|
|
48
|
+
@property
|
|
49
|
+
def logCount(self) -> dict[str, int]:
|
|
50
|
+
return self._logCount
|
|
51
|
+
|
|
52
|
+
def _effectiveMessageCode(self, messageCodes: tuple[Any] | str) -> str | None:
|
|
53
|
+
"""
|
|
54
|
+
If codes includes EFM, GFM, HMRC, or SBR-coded error then the code chosen (if a sequence)
|
|
55
|
+
corresponds to whether EFM, GFM, HMRC, or SBR validation is in effect.
|
|
56
|
+
"""
|
|
57
|
+
effectiveMessageCode = None
|
|
58
|
+
_validationType = self._modelManager.disclosureSystem.validationType
|
|
59
|
+
_exclusiveTypesPattern = self._modelManager.disclosureSystem.exclusiveTypesPattern
|
|
60
|
+
|
|
61
|
+
for argCode in messageCodes if isinstance(messageCodes,tuple) else (messageCodes,):
|
|
62
|
+
if (isinstance(argCode, ModelValue.QName) or
|
|
63
|
+
(_validationType and argCode and argCode.startswith(_validationType)) or
|
|
64
|
+
(not _exclusiveTypesPattern or _exclusiveTypesPattern.match(argCode or "") == None)):
|
|
65
|
+
effectiveMessageCode = argCode
|
|
66
|
+
break
|
|
67
|
+
return effectiveMessageCode
|
|
68
|
+
|
|
69
|
+
def clear(self) -> None:
|
|
70
|
+
self._errors.clear()
|
|
71
|
+
self._logCount.clear()
|
|
72
|
+
|
|
73
|
+
def isLoggingEffectiveFor(self, logger: logging.Logger, **kwargs: Any) -> bool: # args can be messageCode(s) and level
|
|
74
|
+
assert hasattr(logger, 'messageCodeFilter'), 'messageCodeFilter not set on controller logger.'
|
|
75
|
+
assert hasattr(logger, 'messageLevelFilter'), 'messageLevelFilter not set on controller logger.'
|
|
76
|
+
if "messageCodes" in kwargs or "messageCode" in kwargs:
|
|
77
|
+
if "messageCodes" in kwargs:
|
|
78
|
+
messageCodes = kwargs["messageCodes"]
|
|
79
|
+
else:
|
|
80
|
+
messageCodes = kwargs["messageCode"]
|
|
81
|
+
messageCode = self._effectiveMessageCode(messageCodes)
|
|
82
|
+
codeEffective = (messageCode and
|
|
83
|
+
(not logger.messageCodeFilter or logger.messageCodeFilter.match(messageCode)))
|
|
84
|
+
else:
|
|
85
|
+
codeEffective = True
|
|
86
|
+
if "level" in kwargs and logger.messageLevelFilter:
|
|
87
|
+
levelEffective = logger.messageLevelFilter.match(kwargs["level"].lower())
|
|
88
|
+
else:
|
|
89
|
+
levelEffective = True
|
|
90
|
+
return bool(codeEffective and levelEffective)
|
|
91
|
+
|
|
92
|
+
def log(
|
|
93
|
+
self,
|
|
94
|
+
logger: logging.Logger,
|
|
95
|
+
level: str,
|
|
96
|
+
codes: Any,
|
|
97
|
+
msg: str,
|
|
98
|
+
sourceModelXbrl: ModelXbrl | None = None,
|
|
99
|
+
fileSource: FileSource | None = None,
|
|
100
|
+
entryLoadingUrl: str | None = None,
|
|
101
|
+
logRefObjectProperties: bool = False,
|
|
102
|
+
**args: Any
|
|
103
|
+
) -> None:
|
|
104
|
+
"""Same as error(), but level passed in as argument
|
|
105
|
+
"""
|
|
106
|
+
assert hasattr(logger, 'messageCodeFilter'), 'messageCodeFilter not set on controller logger.'
|
|
107
|
+
messageCodeFilter = getattr(logger, 'messageCodeFilter')
|
|
108
|
+
assert hasattr(logger, 'messageLevelFilter'), 'messageLevelFilter not set on controller logger.'
|
|
109
|
+
messageLevelFilter = getattr(logger, 'messageLevelFilter')
|
|
110
|
+
# determine logCode
|
|
111
|
+
messageCode = self._effectiveMessageCode(codes)
|
|
112
|
+
if messageCode == "asrtNoLog":
|
|
113
|
+
self._errors.append(args["assertionResults"])
|
|
114
|
+
return
|
|
115
|
+
if self.logHasRelevelerPlugin is None:
|
|
116
|
+
self.logHasRelevelerPlugin = hasPluginWithHook("Logging.Severity.Releveler")
|
|
117
|
+
if sourceModelXbrl is not None and self.logHasRelevelerPlugin:
|
|
118
|
+
for pluginXbrlMethod in pluginClassMethods("Logging.Severity.Releveler"):
|
|
119
|
+
level, messageCode = pluginXbrlMethod(sourceModelXbrl, level, messageCode, args) # args must be passed as dict because it may contain modelXbrl or messageCode key value
|
|
120
|
+
if (messageCode and
|
|
121
|
+
(not messageCodeFilter or messageCodeFilter.match(messageCode)) and
|
|
122
|
+
(not messageLevelFilter or messageLevelFilter.match(level.lower()))):
|
|
123
|
+
# note that plugin Logging.Message.Parameters may rewrite messageCode which now occurs after filtering on messageCode
|
|
124
|
+
messageCode, logArgs, extras = self._logArguments(
|
|
125
|
+
messageCode,
|
|
126
|
+
msg,
|
|
127
|
+
args,
|
|
128
|
+
sourceModelXbrl=sourceModelXbrl,
|
|
129
|
+
fileSource=fileSource,
|
|
130
|
+
entryLoadingUrl=entryLoadingUrl,
|
|
131
|
+
logRefObjectProperties=logRefObjectProperties,
|
|
132
|
+
)
|
|
133
|
+
numericLevel = logging._checkLevel(level) #type: ignore[attr-defined]
|
|
134
|
+
self._logCount[numericLevel] = self._logCount.get(numericLevel, 0) + 1
|
|
135
|
+
if numericLevel >= self._errorCaptureLevel:
|
|
136
|
+
try: # if there's a numeric errorCount arg, extend messages codes by count
|
|
137
|
+
self._errors.extend([messageCode] * int(logArgs[1]["errorCount"]))
|
|
138
|
+
except (IndexError, KeyError, ValueError): # no msgArgs, no errorCount, or not int
|
|
139
|
+
self._errors.append(messageCode) # assume one error occurence
|
|
140
|
+
"""@messageCatalog=[]"""
|
|
141
|
+
logger.log(numericLevel, *logArgs, exc_info=args.get("exc_info"), extra=extras)
|
|
142
|
+
|
|
143
|
+
def _logArguments(
|
|
144
|
+
self,
|
|
145
|
+
messageCode: str,
|
|
146
|
+
msg: str,
|
|
147
|
+
codedArgs: dict[str, str],
|
|
148
|
+
sourceModelXbrl: ModelXbrl | None = None,
|
|
149
|
+
fileSource: FileSource | None = None,
|
|
150
|
+
entryLoadingUrl: str | None = None,
|
|
151
|
+
logRefObjectProperties: bool = False,
|
|
152
|
+
) -> Any:
|
|
153
|
+
# Prepares arguments for logger function as per info() below.
|
|
154
|
+
|
|
155
|
+
def propValues(properties: Any) -> Any:
|
|
156
|
+
# deref objects in properties
|
|
157
|
+
return [(p[0], str(p[1])) if len(p) == 2 else (p[0], str(p[1]), propValues(p[2]))
|
|
158
|
+
for p in properties if 2 <= len(p) <= 3]
|
|
159
|
+
# determine message and extra arguments
|
|
160
|
+
fmtArgs: dict[str, LoggableValue] = {}
|
|
161
|
+
extras: dict[str, Any] = {"messageCode":messageCode}
|
|
162
|
+
modelObjectArgs: tuple[Any, ...] | list[Any] = ()
|
|
163
|
+
sourceModelDocument = getattr(sourceModelXbrl, "modelDocument", None)
|
|
164
|
+
for argName, argValue in codedArgs.items():
|
|
165
|
+
if argName in ("modelObject", "modelXbrl", "modelDocument"):
|
|
166
|
+
if sourceModelDocument is not None:
|
|
167
|
+
entryUrl = sourceModelDocument.uri
|
|
168
|
+
else:
|
|
169
|
+
if entryLoadingUrl is not None:
|
|
170
|
+
entryUrl = entryLoadingUrl
|
|
171
|
+
else:
|
|
172
|
+
assert fileSource is not None, 'Expected FileSource to be available for fallback entry URL.'
|
|
173
|
+
entryUrl = fileSource.url
|
|
174
|
+
refs: list[dict[str, Any]] = []
|
|
175
|
+
modelObjectArgs_complex = argValue if isinstance(argValue, (tuple,list,set)) else (argValue,)
|
|
176
|
+
modelObjectArgs = flattenSequence(modelObjectArgs_complex)
|
|
177
|
+
for arg in modelObjectArgs:
|
|
178
|
+
if arg is not None:
|
|
179
|
+
if isinstance(arg, str):
|
|
180
|
+
objectUrl = arg
|
|
181
|
+
else:
|
|
182
|
+
try:
|
|
183
|
+
objectUrl = arg.modelDocument.displayUri
|
|
184
|
+
except AttributeError:
|
|
185
|
+
try:
|
|
186
|
+
objectUrl = arg.displayUri
|
|
187
|
+
except AttributeError:
|
|
188
|
+
try:
|
|
189
|
+
objectUrl = sourceModelDocument.displayUri # type: ignore[union-attr]
|
|
190
|
+
except AttributeError:
|
|
191
|
+
objectUrl = entryLoadingUrl or ""
|
|
192
|
+
try:
|
|
193
|
+
if objectUrl.endswith("/_IXDS"):
|
|
194
|
+
file = objectUrl[:-6] # inline document set or report package
|
|
195
|
+
elif objectUrl in self._logRefFileRelUris.get(entryUrl, EMPTY_TUPLE):
|
|
196
|
+
file = self._logRefFileRelUris[entryUrl][objectUrl]
|
|
197
|
+
else:
|
|
198
|
+
file = UrlUtil.relativeUri(entryUrl, objectUrl)
|
|
199
|
+
self._logRefFileRelUris[entryUrl][objectUrl] = file
|
|
200
|
+
except:
|
|
201
|
+
file = ""
|
|
202
|
+
ref: dict[str, Any] = {}
|
|
203
|
+
if isinstance(arg,(ModelObject, ObjectPropertyViewWrapper)):
|
|
204
|
+
_arg:ModelObject = arg.modelObject if isinstance(arg, ObjectPropertyViewWrapper) else arg
|
|
205
|
+
if len(modelObjectArgs) > 1 and getattr(arg,"tag",None) == "instance":
|
|
206
|
+
continue # skip IXDS top level element
|
|
207
|
+
fragmentIdentifier = cast(str, XmlUtil.elementFragmentIdentifier(_arg))
|
|
208
|
+
if not hasattr(_arg, 'modelDocument') and _arg.namespaceURI == XbrlConst.svg:
|
|
209
|
+
# This is an embedded SVG document without its own file.
|
|
210
|
+
ref["href"] = "#" + fragmentIdentifier
|
|
211
|
+
else:
|
|
212
|
+
ref["href"] = file + "#" + fragmentIdentifier
|
|
213
|
+
ref["sourceLine"] = _arg.sourceline
|
|
214
|
+
ref["objectId"] = _arg.objectId()
|
|
215
|
+
if logRefObjectProperties:
|
|
216
|
+
try:
|
|
217
|
+
ref["properties"] = propValues(arg.propertyView)
|
|
218
|
+
except AttributeError:
|
|
219
|
+
pass # is a default properties entry appropriate or needed?
|
|
220
|
+
if any(True for m in pluginClassMethods("Logging.Ref.Properties")):
|
|
221
|
+
refProperties: Any = ref.get("properties", {})
|
|
222
|
+
for pluginXbrlMethod in pluginClassMethods("Logging.Ref.Properties"):
|
|
223
|
+
pluginXbrlMethod(arg, refProperties, codedArgs)
|
|
224
|
+
if refProperties:
|
|
225
|
+
ref["properties"] = refProperties
|
|
226
|
+
else:
|
|
227
|
+
ref["href"] = file
|
|
228
|
+
try:
|
|
229
|
+
ref["sourceLine"] = arg.sourceline
|
|
230
|
+
except AttributeError:
|
|
231
|
+
pass # arg may not have sourceline, ignore if so
|
|
232
|
+
if any(True for m in pluginClassMethods("Logging.Ref.Attributes")):
|
|
233
|
+
refAttributes: dict[str, str] = {}
|
|
234
|
+
for pluginXbrlMethod in pluginClassMethods("Logging.Ref.Attributes"):
|
|
235
|
+
pluginXbrlMethod(arg, refAttributes, codedArgs)
|
|
236
|
+
if refAttributes:
|
|
237
|
+
ref["customAttributes"] = refAttributes
|
|
238
|
+
refs.append(ref)
|
|
239
|
+
extras["refs"] = refs
|
|
240
|
+
elif argName == "sourceFileLine":
|
|
241
|
+
# sourceFileLines is pairs of file and line numbers, e.g., ((file,line),(file2,line2),...)
|
|
242
|
+
ref = {}
|
|
243
|
+
if isinstance(argValue, (tuple,list)):
|
|
244
|
+
ref["href"] = str(argValue[0])
|
|
245
|
+
if len(argValue) > 1 and argValue[1]:
|
|
246
|
+
ref["sourceLine"] = str(argValue[1])
|
|
247
|
+
else:
|
|
248
|
+
ref["href"] = str(argValue)
|
|
249
|
+
extras["refs"] = [ref]
|
|
250
|
+
elif argName == "sourceFileLines":
|
|
251
|
+
# sourceFileLines is tuple/list of pairs of file and line numbers, e.g., ((file,line),(file2,line2),...)
|
|
252
|
+
sf_refs: list[dict[str, str]] = []
|
|
253
|
+
argvalues: tuple[Any, ...] | list[Any] = argValue if isinstance(argValue, (tuple, list)) else (argValue,)
|
|
254
|
+
for arg in argvalues:
|
|
255
|
+
ref = {}
|
|
256
|
+
if isinstance(arg, (tuple, list)):
|
|
257
|
+
arg_: tuple[Any, ...] | list[Any] = arg
|
|
258
|
+
ref["href"] = str(arg_[0])
|
|
259
|
+
if len(arg_) > 1 and arg_[1]:
|
|
260
|
+
ref["sourceLine"] = str(arg_[1])
|
|
261
|
+
else:
|
|
262
|
+
ref["href"] = str(arg)
|
|
263
|
+
sf_refs.append(ref)
|
|
264
|
+
extras["refs"] = sf_refs
|
|
265
|
+
elif argName == "sourceLine":
|
|
266
|
+
if isinstance(argValue, int): # must be sortable with int's in logger
|
|
267
|
+
extras["sourceLine"] = argValue
|
|
268
|
+
elif argName not in ("exc_info", "messageCodes"):
|
|
269
|
+
fmtArgs[argName] = self._loggableValue(argValue) # dereference anything not loggable
|
|
270
|
+
|
|
271
|
+
if "refs" not in extras:
|
|
272
|
+
if sourceModelDocument is not None:
|
|
273
|
+
file = sourceModelDocument.displayUri
|
|
274
|
+
else:
|
|
275
|
+
if entryLoadingUrl is not None:
|
|
276
|
+
file = os.path.basename(entryLoadingUrl)
|
|
277
|
+
else:
|
|
278
|
+
file = ""
|
|
279
|
+
extras["refs"] = [{"href": file}]
|
|
280
|
+
for pluginXbrlMethod in pluginClassMethods("Logging.Message.Parameters"):
|
|
281
|
+
# plug in can rewrite msg string or return msg if not altering msg
|
|
282
|
+
msg = pluginXbrlMethod(messageCode, msg, modelObjectArgs, fmtArgs) or msg
|
|
283
|
+
return (messageCode,
|
|
284
|
+
(msg, fmtArgs) if fmtArgs else (msg,),
|
|
285
|
+
extras)
|
|
286
|
+
|
|
287
|
+
def _loggableValue(self, argValue: Any) -> LoggableValue: # must be dereferenced and not related to object lifetimes
|
|
288
|
+
if argValue is None:
|
|
289
|
+
return "(none)"
|
|
290
|
+
if isinstance(argValue, bool):
|
|
291
|
+
return str(argValue).lower() # show lower case true/false xml values
|
|
292
|
+
if isinstance(argValue, int):
|
|
293
|
+
# need locale-dependent formatting
|
|
294
|
+
return format_string(self._modelManager.locale, '%i', argValue)
|
|
295
|
+
if isinstance(argValue, (float, Decimal)):
|
|
296
|
+
# need locale-dependent formatting
|
|
297
|
+
return format_string(self._modelManager.locale, '%f', argValue)
|
|
298
|
+
if isinstance(argValue, tuple):
|
|
299
|
+
return tuple(self._loggableValue(x) for x in argValue)
|
|
300
|
+
if isinstance(argValue, list):
|
|
301
|
+
return [self._loggableValue(x) for x in argValue]
|
|
302
|
+
if isinstance(argValue, set):
|
|
303
|
+
return {self._loggableValue(x) for x in argValue}
|
|
304
|
+
if isinstance(argValue, dict):
|
|
305
|
+
return dict((self._loggableValue(k), self._loggableValue(v)) for k, v in argValue.items())
|
|
306
|
+
return str(argValue)
|
arelle/ModelRelationshipSet.py
CHANGED
|
@@ -288,7 +288,7 @@ class ModelRelationshipSet:
|
|
|
288
288
|
|
|
289
289
|
# if modelFrom and modelTo are provided determine that they have specified relationship
|
|
290
290
|
# if only modelFrom, determine that there are relationships present of specified axis
|
|
291
|
-
def isRelated(self, modelFrom, axis, modelTo=None, visited=None, isDRS=False, consecutiveLinkrole=False): # either model concept or qname
|
|
291
|
+
def isRelated(self, modelFrom, axis, modelTo=None, visited=None, isDRS=False, consecutiveLinkrole=False) -> bool: # either model concept or qname
|
|
292
292
|
assert self.modelXbrl is not None
|
|
293
293
|
if getattr(self.modelXbrl, "isSupplementalIxdsTarget", False):
|
|
294
294
|
if modelFrom is not None and modelFrom.modelXbrl != self.modelXbrl:
|