arelle-release 2.37.22__py3-none-any.whl → 2.37.25__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of arelle-release might be problematic. Click here for more details.

Files changed (28) hide show
  1. arelle/ModelRelationshipSet.py +3 -0
  2. arelle/Updater.py +7 -3
  3. arelle/ValidateDuplicateFacts.py +13 -7
  4. arelle/XbrlConst.py +22 -0
  5. arelle/_version.py +2 -2
  6. arelle/plugin/validate/DBA/rules/fr.py +10 -10
  7. arelle/plugin/validate/DBA/rules/th.py +1 -1
  8. arelle/plugin/validate/ESEF/ESEF_Current/ValidateXbrlFinally.py +21 -20
  9. arelle/plugin/validate/NL/LinkbaseType.py +17 -0
  10. arelle/plugin/validate/NL/PluginValidationDataExtension.py +155 -4
  11. arelle/plugin/validate/NL/resources/config.xml +6 -0
  12. arelle/plugin/validate/NL/rules/fr_kvk.py +1 -1
  13. arelle/plugin/validate/NL/rules/nl_kvk.py +656 -22
  14. arelle/utils/validate/DetectScriptsInXhtml.py +1 -4
  15. arelle/utils/validate/ESEFImage.py +274 -0
  16. {arelle_release-2.37.22.dist-info → arelle_release-2.37.25.dist-info}/METADATA +1 -1
  17. {arelle_release-2.37.22.dist-info → arelle_release-2.37.25.dist-info}/RECORD +27 -27
  18. tests/integration_tests/validation/conformance_suite_configurations/efm_current.py +2 -2
  19. tests/integration_tests/validation/conformance_suite_configurations/nl_inline_2024.py +52 -28
  20. tests/integration_tests/validation/conformance_suite_configurations/nl_inline_2024_gaap_other.py +10 -0
  21. tests/resources/conformance_suites_timing/efm_current.json +8499 -8583
  22. tests/unit_tests/arelle/plugin/validate/ESEF/ESEF_Current/test_validate_css_url.py +10 -2
  23. tests/unit_tests/arelle/test_updater.py +43 -14
  24. arelle/plugin/validate/ESEF/ESEF_Current/Image.py +0 -213
  25. {arelle_release-2.37.22.dist-info → arelle_release-2.37.25.dist-info}/WHEEL +0 -0
  26. {arelle_release-2.37.22.dist-info → arelle_release-2.37.25.dist-info}/entry_points.txt +0 -0
  27. {arelle_release-2.37.22.dist-info → arelle_release-2.37.25.dist-info}/licenses/LICENSE.md +0 -0
  28. {arelle_release-2.37.22.dist-info → arelle_release-2.37.25.dist-info}/top_level.txt +0 -0
@@ -9,11 +9,19 @@ class TestValidateCssUrl(TestCase):
9
9
  validateCssUrl(
10
10
  '* { background: url("http://example.com") }',
11
11
  MagicMock(), modelXbrl, MagicMock(), MagicMock(), MagicMock())
12
- self.assertEqual(modelXbrl.error.call_args.args[0], 'ESEF.4.1.6.xHTMLDocumentContainsExternalReferences')
12
+ expected = dict(
13
+ level='ERROR',
14
+ codes=('ESEF.3.5.1.inlineXbrlDocumentContainsExternalReferences', 'NL.NL-KVK.3.6.2.1.inlineXbrlDocumentContainsExternalReferences'),
15
+ )
16
+ self.assertLessEqual(expected.items(), modelXbrl.log.call_args.kwargs.items())
13
17
 
14
18
  def test_url_token(self) -> None:
15
19
  modelXbrl = MagicMock()
16
20
  validateCssUrl(
17
21
  '* { background: url(http://example.com) }',
18
22
  MagicMock(), modelXbrl, MagicMock(), MagicMock(), MagicMock())
19
- self.assertEqual(modelXbrl.error.call_args.args[0], 'ESEF.4.1.6.xHTMLDocumentContainsExternalReferences')
23
+ expected = dict(
24
+ level='ERROR',
25
+ codes=('ESEF.3.5.1.inlineXbrlDocumentContainsExternalReferences', 'NL.NL-KVK.3.6.2.1.inlineXbrlDocumentContainsExternalReferences'),
26
+ )
27
+ self.assertLessEqual(expected.items(), modelXbrl.log.call_args.kwargs.items())
@@ -13,8 +13,11 @@ import pytest
13
13
  from arelle import Updater
14
14
  from arelle.Updater import ArelleRelease, ArelleVersion
15
15
 
16
- MACOS_DOWNLOAD_URL = (
17
- "https://github.com/Arelle/Arelle/releases/download/2.1.3/arelle-macos-2.1.3.dmg"
16
+ MACOS_ARM64_DOWNLOAD_URL = (
17
+ "https://github.com/Arelle/Arelle/releases/download/2.1.3/arelle-macos-arm64-2.1.3.dmg"
18
+ )
19
+ MACOS_X64_DOWNLOAD_URL = (
20
+ "https://github.com/Arelle/Arelle/releases/download/2.1.3/arelle-macos-x64-2.1.3.dmg"
18
21
  )
19
22
  WINDOWS_DOWNLOAD_URL = (
20
23
  "https://github.com/Arelle/Arelle/releases/download/2.1.3/arelle-win-2.1.3.exe"
@@ -30,7 +33,8 @@ NEW_SEMVER_VERSION = str(NEW_ARELLE_VERSION)
30
33
  def _mockGitHubRelease(
31
34
  tagName: str = NEW_SEMVER_VERSION,
32
35
  assetUrls: tuple[str] = (
33
- MACOS_DOWNLOAD_URL,
36
+ MACOS_ARM64_DOWNLOAD_URL,
37
+ MACOS_X64_DOWNLOAD_URL,
34
38
  WINDOWS_DOWNLOAD_URL,
35
39
  OTHER_DOWNLOAD_URL,
36
40
  ),
@@ -105,12 +109,37 @@ class TestArelleVersion:
105
109
 
106
110
  class TestUpdater:
107
111
  @patch("sys.platform", "darwin")
112
+ @patch("platform.machine")
113
+ @patch("tkinter.messagebox.showinfo")
114
+ @patch("tkinter.messagebox.showwarning")
115
+ def test_check_for_updates_macos(self, showWarning, showInfo, machine):
116
+ machine.return_value = "x64"
117
+ arelleRelease = ArelleRelease(
118
+ version=NEW_ARELLE_VERSION,
119
+ downloadUrl=MACOS_X64_DOWNLOAD_URL,
120
+ )
121
+ cntlr = _mockCntlrWinMain()
122
+
123
+ Updater._checkForUpdates(cntlr)
124
+
125
+ assert not showInfo.called
126
+ assert not showWarning.called
127
+ assert not cntlr.uiThreadQueue.empty()
128
+ assert cntlr.uiThreadQueue.get_nowait() == (
129
+ Updater._checkUpdateUrl,
130
+ [cntlr, arelleRelease],
131
+ )
132
+ assert cntlr.uiThreadQueue.empty()
133
+
134
+ @patch("sys.platform", "darwin")
135
+ @patch("platform.machine")
108
136
  @patch("tkinter.messagebox.showinfo")
109
137
  @patch("tkinter.messagebox.showwarning")
110
- def test_check_for_updates_macos(self, showWarning, showInfo):
138
+ def test_check_for_updates_macos(self, showWarning, showInfo, machine):
139
+ machine.return_value = "arm64"
111
140
  arelleRelease = ArelleRelease(
112
141
  version=NEW_ARELLE_VERSION,
113
- downloadUrl=MACOS_DOWNLOAD_URL,
142
+ downloadUrl=MACOS_ARM64_DOWNLOAD_URL,
114
143
  )
115
144
  cntlr = _mockCntlrWinMain()
116
145
 
@@ -219,7 +248,7 @@ class TestUpdater:
219
248
  ):
220
249
  arelleRelease = ArelleRelease(
221
250
  version=NEW_ARELLE_VERSION,
222
- downloadUrl=MACOS_DOWNLOAD_URL,
251
+ downloadUrl=MACOS_ARM64_DOWNLOAD_URL,
223
252
  )
224
253
  cntlr = _mockCntlrWinMain()
225
254
  version.version = OLD_SEMVER_VERSION
@@ -275,7 +304,7 @@ class TestUpdater:
275
304
  ):
276
305
  arelleRelease = ArelleRelease(
277
306
  version=NEW_ARELLE_VERSION,
278
- downloadUrl=MACOS_DOWNLOAD_URL,
307
+ downloadUrl=MACOS_ARM64_DOWNLOAD_URL,
279
308
  )
280
309
  cntlr = _mockCntlrWinMain()
281
310
  version.version = OLD_SEMVER_VERSION
@@ -303,7 +332,7 @@ class TestUpdater:
303
332
  ):
304
333
  arelleRelease = ArelleRelease(
305
334
  version=OLD_ARELLE_VERSION,
306
- downloadUrl=MACOS_DOWNLOAD_URL,
335
+ downloadUrl=MACOS_ARM64_DOWNLOAD_URL,
307
336
  )
308
337
  cntlr = _mockCntlrWinMain()
309
338
  version.version = NEW_SEMVER_VERSION
@@ -339,7 +368,7 @@ class TestUpdater:
339
368
  ):
340
369
  arelleRelease = ArelleRelease(
341
370
  version=updateVersion,
342
- downloadUrl=MACOS_DOWNLOAD_URL,
371
+ downloadUrl=MACOS_ARM64_DOWNLOAD_URL,
343
372
  )
344
373
  cntlr = _mockCntlrWinMain()
345
374
  version.version = currentVersion
@@ -361,7 +390,7 @@ class TestUpdater:
361
390
  ):
362
391
  arelleRelease = ArelleRelease(
363
392
  version=NEW_ARELLE_VERSION,
364
- downloadUrl=MACOS_DOWNLOAD_URL,
393
+ downloadUrl=MACOS_ARM64_DOWNLOAD_URL,
365
394
  )
366
395
  cntlr = _mockCntlrWinMain()
367
396
  version.version = "invalid version string"
@@ -378,7 +407,7 @@ class TestUpdater:
378
407
  def test_download(self, showWarning, rename):
379
408
  arelleRelease = ArelleRelease(
380
409
  version=NEW_ARELLE_VERSION,
381
- downloadUrl=MACOS_DOWNLOAD_URL,
410
+ downloadUrl=MACOS_ARM64_DOWNLOAD_URL,
382
411
  )
383
412
  cntlr = _mockCntlrWinMain(
384
413
  tmpDownloadFilename=os.path.normcase("/tmp/path/tmpfile"),
@@ -390,7 +419,7 @@ class TestUpdater:
390
419
  assert not cntlr.uiThreadQueue.empty()
391
420
  assert cntlr.uiThreadQueue.get_nowait() == (
392
421
  Updater._install,
393
- [cntlr, os.path.normcase("/tmp/path/arelle-macos-2.1.3.dmg")],
422
+ [cntlr, os.path.normcase("/tmp/path/arelle-macos-arm64-2.1.3.dmg")],
394
423
  )
395
424
  assert cntlr.uiThreadQueue.empty()
396
425
 
@@ -411,7 +440,7 @@ class TestUpdater:
411
440
  def test_download_failed(self, showWarning, rename):
412
441
  arelleRelease = ArelleRelease(
413
442
  version=NEW_ARELLE_VERSION,
414
- downloadUrl=MACOS_DOWNLOAD_URL,
443
+ downloadUrl=MACOS_ARM64_DOWNLOAD_URL,
415
444
  )
416
445
  cntlr = _mockCntlrWinMain(
417
446
  tmpDownloadFilename=None,
@@ -427,7 +456,7 @@ class TestUpdater:
427
456
  def test_download_process_failed(self, showWarning, rename):
428
457
  arelleRelease = ArelleRelease(
429
458
  version=NEW_ARELLE_VERSION,
430
- downloadUrl=MACOS_DOWNLOAD_URL,
459
+ downloadUrl=MACOS_ARM64_DOWNLOAD_URL,
431
460
  )
432
461
  cntlr = _mockCntlrWinMain()
433
462
  rename.side_effect = OSError()
@@ -1,213 +0,0 @@
1
- """
2
- See COPYRIGHT.md for copyright information.
3
- """
4
- from __future__ import annotations
5
-
6
- import binascii
7
- import os
8
- from typing import cast
9
- from urllib.parse import unquote
10
-
11
- from arelle import ModelDocument
12
- from lxml.etree import XML, XMLSyntaxError
13
- from lxml.etree import _Element
14
-
15
- from arelle.ModelObjectFactory import parser
16
- from arelle.ModelXbrl import ModelXbrl
17
- from arelle.UrlUtil import decodeBase64DataImage, scheme
18
- from arelle.ValidateFilingText import parseImageDataURL, validateGraphicHeaderType
19
- from arelle.ValidateXbrl import ValidateXbrl
20
- from arelle.typing import TypeGetText
21
- from ..Const import supportedImgTypes
22
- from ..Util import getDisclosureSystemYear
23
-
24
- _: TypeGetText # Handle gettext
25
-
26
-
27
- # check image contents against mime/file ext and for Steganography
28
- def validateImage(
29
- baseUrl: str | None,
30
- image: str,
31
- modelXbrl: ModelXbrl,
32
- val: ValidateXbrl,
33
- elts: _Element | list[_Element],
34
- evaluatedMsg: str,
35
- contentOtherThanXHTMLGuidance: str,
36
- ) -> None:
37
- """
38
- image: either an url or base64 in data:image style
39
- """
40
- # a list of img elements are maintained because an SVG can reference another SVG
41
- # or other type of image and we need to log the entire reference chain.
42
- if not isinstance(elts, list):
43
- elts = [elts]
44
- minExternalRessourceSize = val.authParam["minExternalResourceSizekB"]
45
- if minExternalRessourceSize != -1:
46
- # transform kb to b
47
- minExternalRessourceSize = minExternalRessourceSize * 1024
48
- if scheme(image) in ("http", "https", "ftp"):
49
- modelXbrl.error("ESEF.4.1.6.xHTMLDocumentContainsExternalReferences" if val.unconsolidated
50
- else "ESEF.3.5.1.inlineXbrlDocumentContainsExternalReferences",
51
- _("Inline XBRL instance documents MUST NOT contain any reference pointing to resources outside the reporting package: %(element)s"),
52
- modelObject=elts, element=elts[0].tag, evaluatedMsg=evaluatedMsg,
53
- messageCodes=("ESEF.3.5.1.inlineXbrlDocumentContainsExternalReferences",
54
- "ESEF.4.1.6.xHTMLDocumentContainsExternalReferences"))
55
- elif image.startswith("data:image"):
56
- dataURLParts = parseImageDataURL(image)
57
- if not dataURLParts or not dataURLParts.isBase64:
58
- modelXbrl.warning(f"{contentOtherThanXHTMLGuidance}.embeddedImageNotUsingBase64Encoding",
59
- _("Images included in the XHTML document SHOULD be base64 encoded: %(src)s."),
60
- modelObject=elts, src=image[:128], evaluatedMsg=evaluatedMsg)
61
- if dataURLParts and dataURLParts.mimeSubtype and dataURLParts.data:
62
- checkImageContents(None, modelXbrl, elts, dataURLParts.mimeSubtype, False, unquote(dataURLParts.data), val.consolidated, val)
63
- else:
64
- if not dataURLParts.mimeSubtype:
65
- modelXbrl.error(f"{contentOtherThanXHTMLGuidance}.MIMETypeNotSpecified",
66
- _("Images included in the XHTML document MUST be saved with MIME type specifying PNG, GIF, SVG or JPG/JPEG formats: %(src)s."),
67
- modelObject=elts, src=image[:128], evaluatedMsg=evaluatedMsg)
68
- elif dataURLParts.mimeSubtype not in ("gif", "jpeg", "png", "svg+xml"):
69
- modelXbrl.error(f"{contentOtherThanXHTMLGuidance}.imageFormatNotSupported",
70
- _("Images included in the XHTML document MUST be saved in PNG, GIF, SVG or JPG/JPEG formats: %(src)s."),
71
- modelObject=elts, src=image[:128], evaluatedMsg=evaluatedMsg)
72
- # check for malicious image contents
73
- try: # allow embedded newlines
74
- imgContents = decodeBase64DataImage(dataURLParts.data)
75
- checkImageContents(None, modelXbrl, elts, str(dataURLParts.mimeSubtype), False, imgContents, val.consolidated, val)
76
- imgContents = b"" # deref, may be very large
77
-
78
- except binascii.Error as err:
79
- modelXbrl.error(f"{contentOtherThanXHTMLGuidance}.embeddedImageNotUsingBase64Encoding",
80
- _("Base64 encoding error %(err)s in image source: %(src)s."),
81
- modelObject=elts, err=str(err), src=image[:128], evaluatedMsg=evaluatedMsg)
82
- else:
83
- # presume it to be an image file, check image contents
84
- try:
85
- base = baseUrl
86
- normalizedUri = modelXbrl.modelManager.cntlr.webCache.normalizeUrl(image, base)
87
- if not modelXbrl.fileSource.isInArchive(normalizedUri):
88
- normalizedUri = modelXbrl.modelManager.cntlr.webCache.getfilename(normalizedUri)
89
- imglen = 0
90
- with modelXbrl.fileSource.file(normalizedUri, binary=True)[0] as fh:
91
- imgContents = cast(bytes, fh.read())
92
- imglen += len(imgContents or '')
93
- checkImageContents(normalizedUri, modelXbrl, elts, os.path.splitext(image)[1], True, imgContents,
94
- val.consolidated, val)
95
- imgContents = b"" # deref, may be very large
96
- if getDisclosureSystemYear(modelXbrl) < 2023 and imglen < minExternalRessourceSize:
97
- modelXbrl.warning(
98
- "%s.imageIncludedAndNotEmbeddedAsBase64EncodedString" % contentOtherThanXHTMLGuidance,
99
- _("Images SHOULD be included in the XHTML document as a base64 encoded string unless their size exceeds the minimum size for the authority (%(maxImageSize)s): %(file)s."),
100
- modelObject=elts, maxImageSize=minExternalRessourceSize, file=os.path.basename(normalizedUri), evaluatedMsg=evaluatedMsg)
101
- except IOError as err:
102
- fileReferencingImage = os.path.basename(baseUrl) if baseUrl else ''
103
- modelXbrl.error(f"{contentOtherThanXHTMLGuidance}.imageFileCannotBeLoaded",
104
- _("Error opening the file '%(src)s' referenced by '%(fileReferencingImage)s': %(error)s"),
105
- modelObject=elts, src=image, fileReferencingImage=fileReferencingImage, error=err, evaluatedMsg=evaluatedMsg)
106
-
107
-
108
- def checkImageContents(
109
- baseURI: str | None,
110
- modelXbrl: ModelXbrl,
111
- imgElts: list[_Element],
112
- imgType: str,
113
- isFile: bool,
114
- data: bytes | str,
115
- consolidated: bool,
116
- val: ValidateXbrl,
117
- ) -> None:
118
- guidance = 'ESEF.2.5.1' if consolidated else 'ESEF.4.1.3'
119
- if "svg" in imgType:
120
- try:
121
- checkSVGContent(baseURI, modelXbrl, imgElts, data, guidance, val)
122
- except XMLSyntaxError as err:
123
- try:
124
- checkSVGContent(baseURI, modelXbrl, imgElts, unquote(data), guidance, val) # Try with utf-8 decoded data as in conformance suite G4-1-3_2/TC2
125
- except XMLSyntaxError:
126
- modelXbrl.error(f"{guidance}.imageFileCannotBeLoaded",
127
- _("Image SVG has XML error %(error)s"),
128
- modelObject=imgElts, error=err)
129
- except UnicodeDecodeError as err:
130
- modelXbrl.error(f"{guidance}.imageFileCannotBeLoaded",
131
- _("Image SVG has XML error %(error)s"),
132
- modelObject=imgElts, error=err)
133
- else:
134
- headerType = validateGraphicHeaderType(data) # type: ignore[arg-type]
135
- if (("gif" not in imgType and headerType == "gif") or
136
- ("jpeg" not in imgType and "jpg" not in imgType and headerType == "jpg") or
137
- ("png" not in imgType and headerType == "png")):
138
- imageDoesNotMatchItsFileExtension = f"{guidance}.imageDoesNotMatchItsFileExtension"
139
- incorrectMIMETypeSpecified = f"{guidance}.incorrectMIMETypeSpecified"
140
- if isFile:
141
- code = imageDoesNotMatchItsFileExtension
142
- message = _("File type %(headerType)s inferred from file signature does not match the file extension %(imgType)s")
143
- else:
144
- code = incorrectMIMETypeSpecified
145
- message = _("File type %(headerType)s inferred from file signature does not match the data URL media subtype (MIME subtype) %(imgType)s")
146
- modelXbrl.error(code, message,
147
- modelObject=imgElts, imgType=imgType, headerType=headerType,
148
- messageCodes=(imageDoesNotMatchItsFileExtension, incorrectMIMETypeSpecified))
149
- elif not any(it in imgType for it in supportedImgTypes[isFile]):
150
- modelXbrl.error(f"{guidance}.imageFormatNotSupported",
151
- _("Images included in the XHTML document MUST be saved in PNG, GIF, SVG or JPEG formats: %(imgType)s is not supported"),
152
- modelObject=imgElts, imgType=imgType)
153
-
154
-
155
- def checkSVGContent(
156
- baseURI: str | None,
157
- modelXbrl: ModelXbrl,
158
- imgElts: list[_Element],
159
- data: bytes | str,
160
- guidance: str,
161
- val: ValidateXbrl,
162
- ) -> None:
163
- if baseURI:
164
- svgDoc = cast(ModelDocument.ModelDocument, ModelDocument.load(modelXbrl, baseURI, referringElement=imgElts[0]))
165
- elt = svgDoc.xmlRootElement
166
- else:
167
- _parser, _ignored, _ignored = parser(modelXbrl, baseURI)
168
- elt = XML(data, parser=_parser)
169
- checkSVGContentElt(elt, baseURI, modelXbrl, imgElts, guidance, val)
170
-
171
-
172
- def getHref(elt:_Element) -> str:
173
- simple_href = elt.get("href", "").strip()
174
- if len(simple_href) > 0:
175
- return simple_href
176
- else:
177
- # 'xlink:href' is deprecated but still used by some SVG generators
178
- return elt.get("{http://www.w3.org/1999/xlink}href", "").strip()
179
-
180
-
181
- def checkSVGContentElt(
182
- elt: _Element,
183
- baseUrl: str | None,
184
- modelXbrl: ModelXbrl,
185
- imgElts: list[_Element],
186
- guidance: str,
187
- val: ValidateXbrl,
188
- ) -> None:
189
- rootElement = True
190
- for elt in elt.iter():
191
- if rootElement:
192
- if elt.tag != "{http://www.w3.org/2000/svg}svg":
193
- modelXbrl.error(f"{guidance}.imageFileCannotBeLoaded",
194
- _("Image SVG has root element which is not svg"),
195
- modelObject=imgElts)
196
- rootElement = False
197
- # Comments, processing instructions, and maybe other special constructs don't have string tags.
198
- if not isinstance(elt.tag, str):
199
- continue
200
- eltTag = elt.tag.rpartition("}")[2] # strip namespace
201
- if eltTag == "image":
202
- imgElts = [*imgElts, elt]
203
- validateImage(baseUrl, getHref(elt), modelXbrl, val, imgElts, "", guidance)
204
- if eltTag in ("object", "script", "audio", "foreignObject", "iframe", "image", "use", "video"):
205
- href = elt.get("href","")
206
- if eltTag in ("object", "script") or "javascript:" in href:
207
- modelXbrl.error(f"{guidance}.executableCodePresent",
208
- _("Inline XBRL images MUST NOT contain executable code: %(element)s"),
209
- modelObject=imgElts, element=eltTag)
210
- elif scheme(href) in ("http", "https", "ftp"):
211
- modelXbrl.error(f"{guidance}.referencesPointingOutsideOfTheReportingPackagePresent",
212
- _("Inline XBRL instance document [image] MUST NOT contain any reference pointing to resources outside the reporting package: %(element)s"),
213
- modelObject=imgElts, element=eltTag)