arelle-release 2.37.57__py3-none-any.whl → 2.37.58__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/Cntlr.py CHANGED
@@ -161,6 +161,7 @@ class Cntlr:
161
161
  disable_persistent_config: bool = False,
162
162
  betaFeatures: dict[str, bool] | None = None
163
163
  ) -> None:
164
+ self.logger = None
164
165
  if betaFeatures is None:
165
166
  betaFeatures = {}
166
167
  self.betaFeatures = {
@@ -386,15 +386,17 @@ class DialogPluginManager(Toplevel):
386
386
  group = {
387
387
  "ixbrl-viewer": "1", # pip installed Arelle viewer
388
388
  "iXBRLViewerPlugin": "2", # git clone installed Arelle viewer
389
- "Edgar Renderer": "3",
389
+ "EDGAR": "3",
390
+ "Inline XBRL Document Set": "4",
391
+ "XBRL rule processor (xule)": "5",
390
392
  }.get(key)
391
393
  if not group:
392
394
  if key.startswith("Validate"):
393
- group = "4"
395
+ group = "6"
394
396
  elif key.startswith("xbrlDB"):
395
- group = "5"
397
+ group = "7"
396
398
  else:
397
- group = "6"
399
+ group = "8"
398
400
  return group + key.lower()
399
401
 
400
402
  @staticmethod
arelle/ErrorManager.py CHANGED
@@ -180,7 +180,10 @@ class ErrorManager:
180
180
  objectUrl = arg
181
181
  else:
182
182
  try:
183
- objectUrl = arg.modelDocument.displayUri
183
+ if isinstance(arg, ObjectPropertyViewWrapper):
184
+ objectUrl = arg.modelObject.modelDocument.displayUri
185
+ else:
186
+ objectUrl = arg.modelDocument.displayUri
184
187
  except AttributeError:
185
188
  try:
186
189
  objectUrl = arg.displayUri
arelle/PluginManager.py CHANGED
@@ -1,31 +1,41 @@
1
1
  '''
2
2
  See COPYRIGHT.md for copyright information.
3
-
4
- based on pull request 4
5
-
6
3
  '''
7
4
  from __future__ import annotations
8
- import os, sys, types, time, ast, importlib, io, json, gettext, traceback
9
- from dataclasses import dataclass
10
- from importlib.metadata import entry_points, EntryPoint
5
+
6
+ import ast
7
+ import gettext
8
+ from glob import glob
11
9
  import importlib.util
10
+ import json
12
11
  import logging
13
-
12
+ import os
13
+ import sys
14
+ import time
15
+ import traceback
16
+ import types
17
+ from collections import defaultdict
18
+ from collections.abc import Callable, Iterator
19
+ from dataclasses import dataclass
20
+ from importlib.metadata import EntryPoint, entry_points
21
+ from numbers import Number
22
+ from pathlib import Path
14
23
  from types import ModuleType
15
24
  from typing import TYPE_CHECKING, Any, cast
16
- from arelle.Locale import getLanguageCodes
25
+
17
26
  import arelle.FileSource
27
+ from arelle.Locale import getLanguageCodes
18
28
  from arelle.PythonUtil import isLegacyAbs
29
+ from arelle.typing import TypeGetText
19
30
  from arelle.UrlUtil import isAbsolute
20
- from pathlib import Path
21
- from collections import OrderedDict, defaultdict
22
- from collections.abc import Callable, Iterator
23
-
24
31
 
25
32
  if TYPE_CHECKING:
26
33
  # Prevent potential circular import error
27
34
  from .Cntlr import Cntlr
28
35
 
36
+
37
+ _: TypeGetText
38
+
29
39
  PLUGIN_TRACE_FILE = None
30
40
  # PLUGIN_TRACE_FILE = "c:/temp/pluginerr.txt"
31
41
  PLUGIN_TRACE_LEVEL = logging.WARNING
@@ -58,7 +68,7 @@ def init(cntlr: Cntlr, loadPluginConfig: bool = True) -> None:
58
68
  if loadPluginConfig:
59
69
  try:
60
70
  pluginJsonFile = cntlr.userAppDir + os.sep + "plugins.json"
61
- with io.open(pluginJsonFile, 'rt', encoding='utf-8') as f:
71
+ with open(pluginJsonFile, encoding='utf-8') as f:
62
72
  pluginConfig = json.load(f)
63
73
  freshenModuleInfos()
64
74
  except Exception:
@@ -79,29 +89,45 @@ def reset() -> None: # force reloading modules and plugin infos
79
89
  pluginMethodsForClasses.clear() # dict by class of list of ordered callable function objects
80
90
 
81
91
  def orderedPluginConfig():
82
- return OrderedDict(
83
- (('modules',OrderedDict((moduleName,
84
- OrderedDict(sorted(moduleInfo.items(),
85
- key=lambda k: {'name': '01',
86
- 'status': '02',
87
- 'version': '03',
88
- 'fileDate': '04', 'version': '05',
89
- 'description': '05',
90
- 'moduleURL': '06',
91
- 'localeURL': '07',
92
- 'localeDomain': '08',
93
- 'license': '09',
94
- 'author': '10',
95
- 'copyright': '11',
96
- 'classMethods': '12'}.get(k[0],k[0]))))
97
- for moduleName, moduleInfo in sorted(pluginConfig['modules'].items()))),
98
- ('classes',OrderedDict(sorted(pluginConfig['classes'].items())))))
92
+ fieldOrder = [
93
+ 'name',
94
+ 'status',
95
+ 'fileDate',
96
+ 'version',
97
+ 'description',
98
+ 'moduleURL',
99
+ 'localeURL',
100
+ 'localeDomain',
101
+ 'license',
102
+ 'author',
103
+ 'copyright',
104
+ 'classMethods',
105
+ ]
106
+ priorityIndex = {k: i for i, k in enumerate(fieldOrder)}
107
+
108
+ def sortModuleInfo(moduleInfo):
109
+ # Prioritize known fields by the index in fieldOrder; sort others alphabetically
110
+ orderedKeys = sorted(
111
+ moduleInfo.keys(),
112
+ key=lambda k: (priorityIndex.get(k, len(priorityIndex)), k)
113
+ )
114
+ return {k: moduleInfo[k] for k in orderedKeys}
115
+
116
+ orderedModules = {
117
+ moduleName: sortModuleInfo(pluginConfig['modules'][moduleName])
118
+ for moduleName in sorted(pluginConfig['modules'].keys())
119
+ }
120
+
121
+ return {
122
+ 'modules': orderedModules,
123
+ 'classes': dict(sorted(pluginConfig['classes'].items()))
124
+ }
99
125
 
100
126
  def save(cntlr: Cntlr) -> None:
101
127
  global pluginConfigChanged
102
128
  if pluginConfigChanged and cntlr.hasFileSystem and not cntlr.disablePersistentConfig:
103
129
  pluginJsonFile = cntlr.userAppDir + os.sep + "plugins.json"
104
- with io.open(pluginJsonFile, 'wt', encoding='utf-8') as f:
130
+ with open(pluginJsonFile, 'w', encoding='utf-8') as f:
105
131
  jsonStr = str(json.dumps(orderedPluginConfig(), ensure_ascii=False, indent=2)) # might not be unicode in 2.7
106
132
  f.write(jsonStr)
107
133
  pluginConfigChanged = False
@@ -155,7 +181,6 @@ moduleInfo = {
155
181
 
156
182
  '''
157
183
 
158
-
159
184
  def logPluginTrace(message: str, level: Number) -> None:
160
185
  """
161
186
  If plugin trace file logging is configured, logs `message` to it.
@@ -274,6 +299,14 @@ def getModuleFilename(moduleURL: str, reload: bool, normalize: bool, base: str |
274
299
  if moduleFilename:
275
300
  # `moduleFilename` normalized to an existing script
276
301
  return moduleFilename, None
302
+ if base and not _isAbsoluteModuleURL(moduleURL):
303
+ # Search for a matching plugin deeper in the plugin directory tree.
304
+ # Handles cases where a plugin exists in a nested structure, such as
305
+ # when a developer clones an entire repository into the plugin directory.
306
+ # Example: arelle/plugin/xule/plugin/xule/__init__.py
307
+ for path in glob("**/" + moduleURL.replace('\\', '/'), recursive=True):
308
+ if normalizedPath := normalizeModuleFilename(path):
309
+ return normalizedPath, None
277
310
  # `moduleFilename` did not map to a local filepath or did not normalize to a script
278
311
  # Try using `moduleURL` to search for pip-installed entry point
279
312
  entryPointRef = EntryPointRef.get(moduleURL)
@@ -416,7 +449,7 @@ def moduleModuleInfo(
416
449
 
417
450
  if moduleFilename:
418
451
  try:
419
- logPluginTrace("Scanning module for plug-in info: {}".format(moduleFilename), logging.INFO)
452
+ logPluginTrace(f"Scanning module for plug-in info: {moduleFilename}", logging.INFO)
420
453
  moduleInfo = parsePluginInfo(moduleURL, moduleFilename, entryPoint)
421
454
  if moduleInfo is None:
422
455
  return None
@@ -426,38 +459,50 @@ def moduleModuleInfo(
426
459
  del moduleInfo["importURLs"]
427
460
  moduleImports = moduleInfo["moduleImports"]
428
461
  del moduleInfo["moduleImports"]
429
- _moduleImportsSubtree = False
462
+ moduleImportsSubtree = False
430
463
  mergedImportURLs = []
431
464
 
432
- for _url in importURLs:
433
- if _url.startswith("module_import"):
465
+ for url in importURLs:
466
+ if url.startswith("module_import"):
434
467
  for moduleImport in moduleImports:
435
468
  mergedImportURLs.append(moduleImport + ".py")
436
- if _url == "module_import_subtree":
437
- _moduleImportsSubtree = True
438
- elif _url == "module_subtree":
469
+ if url == "module_import_subtree":
470
+ moduleImportsSubtree = True
471
+ elif url == "module_subtree":
439
472
  for _dir in os.listdir(moduleDir):
440
- _subtreeModule = os.path.join(moduleDir,_dir)
441
- if os.path.isdir(_subtreeModule) and _dir != "__pycache__":
442
- mergedImportURLs.append(_subtreeModule)
473
+ subtreeModule = os.path.join(moduleDir,_dir)
474
+ if os.path.isdir(subtreeModule) and _dir != "__pycache__":
475
+ mergedImportURLs.append(subtreeModule)
443
476
  else:
444
- mergedImportURLs.append(_url)
445
- if parentImportsSubtree and not _moduleImportsSubtree:
446
- _moduleImportsSubtree = True
477
+ mergedImportURLs.append(url)
478
+ if parentImportsSubtree and not moduleImportsSubtree:
479
+ moduleImportsSubtree = True
447
480
  for moduleImport in moduleImports:
448
481
  mergedImportURLs.append(moduleImport + ".py")
449
482
  imports = []
450
- for _url in mergedImportURLs:
451
- if isAbsolute(_url) or isLegacyAbs(_url):
452
- _importURL = _url # URL is absolute http or local file system
453
- else: # check if exists relative to this module's directory
454
- _importURL = os.path.join(os.path.dirname(moduleURL), os.path.normpath(_url))
455
- if not os.path.exists(_importURL): # not relative to this plugin, assume standard plugin base
456
- _importURL = _url # moduleModuleInfo adjusts relative URL to plugin base
457
- _importModuleInfo = moduleModuleInfo(moduleURL=_importURL, reload=reload, parentImportsSubtree=_moduleImportsSubtree)
458
- if _importModuleInfo:
459
- _importModuleInfo["isImported"] = True
460
- imports.append(_importModuleInfo)
483
+ for url in mergedImportURLs:
484
+ importURL = url
485
+ if not _isAbsoluteModuleURL(url):
486
+ # Handle relative imports when plugin is loaded from external directory.
487
+ # When EDGAR/render imports EDGAR/validate, this works if EDGAR is in the plugin directory
488
+ # but fails if loaded externally (e.g., dev repo clone at /dev/path/to/EDGAR/).
489
+ # Solution: Find common path segments to resolve /dev/path/to/EDGAR/validate
490
+ # from the importing module at /dev/path/to/EDGAR/render.
491
+ modulePath = Path(moduleFilename)
492
+ importPath = Path(url)
493
+ if importPath.parts:
494
+ importFirstPart = importPath.parts[0]
495
+ for i, modulePathPart in enumerate(reversed(modulePath.parts)):
496
+ if modulePathPart != importFirstPart:
497
+ continue
498
+ # Found a potential branching point, construct and check a new path
499
+ candidateImportURL = str(modulePath.parents[i] / importPath)
500
+ if normalizeModuleFilename(candidateImportURL):
501
+ importURL = candidateImportURL
502
+ importModuleInfo = moduleModuleInfo(moduleURL=importURL, reload=reload, parentImportsSubtree=moduleImportsSubtree)
503
+ if importModuleInfo:
504
+ importModuleInfo["isImported"] = True
505
+ imports.append(importModuleInfo)
461
506
  moduleInfo["imports"] = imports
462
507
  logPluginTrace(f"Successful module plug-in info: {moduleFilename}", logging.INFO)
463
508
  return moduleInfo
@@ -477,43 +522,30 @@ def moduleInfo(pluginInfo):
477
522
  moduleInfo.getdefault('classes', []).append(name)
478
523
 
479
524
 
480
- def _get_name_dir_prefix(
481
- controller: Cntlr,
482
- pluginBase: str,
483
- moduleURL: str,
484
- packagePrefix: str = "",
485
- ) -> tuple[str, str, str] | tuple[None, None, None]:
486
- """Get the name, directory and prefix of a module."""
487
- moduleFilename: str
488
- moduleDir: str
489
- packageImportPrefix: str
525
+ def _isAbsoluteModuleURL(moduleURL: str) -> bool:
526
+ return isAbsolute(moduleURL) or isLegacyAbs(moduleURL)
490
527
 
491
- moduleFilename = controller.webCache.getfilename(
492
- url=moduleURL, normalize=True, base=pluginBase, allowTransformation=False
493
- )
494
528
 
495
- if moduleFilename:
496
- if os.path.basename(moduleFilename) == "__init__.py" and os.path.isfile(
497
- moduleFilename
498
- ):
499
- moduleFilename = os.path.dirname(
500
- moduleFilename
501
- ) # want just the dirpart of package
502
-
503
- if os.path.isdir(moduleFilename) and os.path.isfile(
504
- os.path.join(moduleFilename, "__init__.py")
505
- ):
506
- moduleDir = os.path.dirname(moduleFilename)
507
- moduleName = os.path.basename(moduleFilename)
508
- packageImportPrefix = moduleName + "."
509
- else:
510
- moduleName = os.path.basename(moduleFilename).partition(".")[0]
511
- moduleDir = os.path.dirname(moduleFilename)
512
- packageImportPrefix = packagePrefix
529
+ def _get_name_dir_prefix(modulePath: Path, packagePrefix: str = "") -> tuple[str, str, str] | tuple[None, None, None]:
530
+ """Get the name, directory and prefix of a module."""
531
+ moduleName = None
532
+ moduleDir = None
533
+ packageImportPrefix = None
534
+ initFileName = "__init__.py"
535
+
536
+ if modulePath.is_file() and modulePath.name == initFileName:
537
+ modulePath = modulePath.parent
513
538
 
514
- return (moduleName, moduleDir, packageImportPrefix)
539
+ if modulePath.is_dir() and (modulePath / initFileName).is_file():
540
+ moduleName = modulePath.name
541
+ moduleDir = str(modulePath.parent)
542
+ packageImportPrefix = moduleName + "."
543
+ elif modulePath.is_file() and modulePath.suffix == ".py":
544
+ moduleName = modulePath.stem
545
+ moduleDir = str(modulePath.parent)
546
+ packageImportPrefix = packagePrefix
515
547
 
516
- return (None, None, None)
548
+ return (moduleName, moduleDir, packageImportPrefix)
517
549
 
518
550
  def _get_location(moduleDir: str, moduleName: str) -> str:
519
551
  """Get the file name of a plugin."""
@@ -543,13 +575,9 @@ def _find_and_load_module(moduleDir: str, moduleName: str) -> ModuleType | None:
543
575
  def loadModule(moduleInfo: dict[str, Any], packagePrefix: str="") -> None:
544
576
  name = moduleInfo['name']
545
577
  moduleURL = moduleInfo['moduleURL']
578
+ modulePath = Path(moduleInfo['path'])
546
579
 
547
- moduleName, moduleDir, packageImportPrefix = _get_name_dir_prefix(
548
- controller=_cntlr,
549
- pluginBase=_pluginBase,
550
- moduleURL=moduleURL,
551
- packagePrefix=packagePrefix,
552
- )
580
+ moduleName, moduleDir, packageImportPrefix = _get_name_dir_prefix(modulePath, packagePrefix)
553
581
 
554
582
  if all(p is None for p in [moduleName, moduleDir, packageImportPrefix]):
555
583
  _cntlr.addToLog(message=_ERROR_MESSAGE_IMPORT_TEMPLATE.format(name), level=logging.ERROR)
@@ -566,10 +594,12 @@ def loadModule(moduleInfo: dict[str, Any], packagePrefix: str="") -> None:
566
594
  localeDir = os.path.dirname(module.__file__) + os.sep + pluginInfo['localeURL']
567
595
  try:
568
596
  _gettext = gettext.translation(pluginInfo['localeDomain'], localeDir, getLanguageCodes())
569
- except IOError:
570
- _gettext = lambda x: x # no translation
597
+ except OSError:
598
+ def _gettext(x):
599
+ return x # no translation
571
600
  else:
572
- _gettext = lambda x: x
601
+ def _gettext(x):
602
+ return x
573
603
  for key, value in pluginInfo.items():
574
604
  if key == 'name':
575
605
  if name:
@@ -627,8 +657,7 @@ def pluginClassMethods(className: str) -> Iterator[Callable[..., Any]]:
627
657
  if className in pluginInfo:
628
658
  pluginMethodsForClass.append(pluginInfo[className])
629
659
  pluginMethodsForClasses[className] = pluginMethodsForClass
630
- for method in pluginMethodsForClass:
631
- yield method
660
+ yield from pluginMethodsForClass
632
661
 
633
662
 
634
663
  def addPluginModule(name: str) -> dict[str, Any] | None:
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.57'
32
- __version_tuple__ = version_tuple = (2, 37, 57)
31
+ __version__ = version = '2.37.58'
32
+ __version_tuple__ = version_tuple = (2, 37, 58)
33
33
 
34
34
  __commit_id__ = commit_id = None
@@ -58,3 +58,22 @@ PROHIBITED_HTML_TAGS = frozenset({
58
58
  'select',
59
59
  'textarea',
60
60
  })
61
+
62
+ NUMERIC_LABEL_ROLES = frozenset({
63
+ 'http://www.xbrl.org/2003/role/positiveLabel',
64
+ 'http://www.xbrl.org/2003/role/positiveTerseLabel',
65
+ 'http://www.xbrl.org/2003/role/positiveVerboseLabel',
66
+ 'http://www.xbrl.org/2003/role/negativeLabel',
67
+ 'http://www.xbrl.org/2003/role/negativeTerseLabel',
68
+ 'http://www.xbrl.org/2003/role/negativeVerboseLabel',
69
+ 'http://www.xbrl.org/2003/role/zeroLabel',
70
+ 'http://www.xbrl.org/2003/role/zeroTerseLabel',
71
+ 'http://www.xbrl.org/2003/role/zeroVerboseLabel',
72
+ 'http://www.xbrl.org/2003/role/totalLabel',
73
+ 'http://www.xbrl.org/2009/role/negatedLabel',
74
+ 'http://www.xbrl.org/2009/role/negatedPeriodEndLabel',
75
+ 'http://www.xbrl.org/2009/role/negatedPeriodStartLabel',
76
+ 'http://www.xbrl.org/2009/role/negatedTotalLabel',
77
+ 'http://www.xbrl.org/2009/role/negatedNetLabel',
78
+ 'http://www.xbrl.org/2009/role/negatedTerseLabel',
79
+ })
@@ -44,6 +44,20 @@ _: TypeGetText
44
44
 
45
45
  _DEBIT_QNAME_PATTERN = regex.compile('.*(Liability|Liabilities|Equity)')
46
46
 
47
+ STANDARD_TAXONOMY_URL_PREFIXES = frozenset((
48
+ 'http://disclosure.edinet-fsa.go.jp/taxonomy/',
49
+ 'https://disclosure.edinet-fsa.go.jp/taxonomy/',
50
+ 'http://www.xbrl.org/20',
51
+ 'https://www.xbrl.org/20',
52
+ 'http://www.xbrl.org/lrr/',
53
+ 'https://www.xbrl.org/lrr/',
54
+ 'http://xbrl.org/20',
55
+ 'https://xbrl.org/20',
56
+ 'http://www.xbrl.org/dtr/',
57
+ 'https://www.xbrl.org/dtr/',
58
+ 'http://www.w3.org/1999/xlink',
59
+ 'https://www.w3.org/1999/xlink'
60
+ ))
47
61
 
48
62
  @dataclass(frozen=True)
49
63
  class UriReference:
@@ -175,6 +189,11 @@ class PluginValidationDataExtension(PluginData):
175
189
  def isCorporateReport(self, modelXbrl: ModelXbrl) -> bool:
176
190
  return self.jpcrpNamespace in modelXbrl.namespaceDocs
177
191
 
192
+ def isExtensionUri(self, uri: str, modelXbrl: ModelXbrl) -> bool:
193
+ if uri.startswith(modelXbrl.uriDir):
194
+ return True
195
+ return not any(uri.startswith(taxonomyUri) for taxonomyUri in STANDARD_TAXONOMY_URL_PREFIXES)
196
+
178
197
  @lru_cache(1)
179
198
  def isStockForm(self, modelXbrl: ModelXbrl) -> bool:
180
199
  formTypes = self.getFormTypes(modelXbrl)
@@ -183,6 +202,18 @@ class PluginValidationDataExtension(PluginData):
183
202
  for formType in formTypes
184
203
  )
185
204
 
205
+ @lru_cache(1)
206
+ def getExtensionConcepts(self, modelXbrl: ModelXbrl) -> list[ModelConcept]:
207
+ """
208
+ Returns a list of extension concepts in the DTS.
209
+ """
210
+ extensionConcepts = []
211
+ for concepts in modelXbrl.nameConcepts.values():
212
+ for concept in concepts:
213
+ if self.isExtensionUri(concept.qname.namespaceURI, modelXbrl):
214
+ extensionConcepts.append(concept)
215
+ return extensionConcepts
216
+
186
217
  def getBalanceSheets(self, modelXbrl: ModelXbrl, statement: Statement) -> list[BalanceSheet]:
187
218
  """
188
219
  :return: Balance sheet data for each context/unit pairing the given statement.
@@ -27,6 +27,7 @@ from arelle.utils.Units import getDuplicateUnitGroups
27
27
  from arelle.utils.validate.Decorator import validation
28
28
  from arelle.utils.validate.Validation import Validation
29
29
  from arelle.utils.validate.ValidationUtil import etreeIterWithDepth
30
+ from ..Constants import NUMERIC_LABEL_ROLES
30
31
  from ..DisclosureSystems import (DISCLOSURE_SYSTEM_EDINET)
31
32
  from ..PluginValidationDataExtension import PluginValidationDataExtension
32
33
 
@@ -702,6 +703,57 @@ def rule_gfm_1_3_20(
702
703
  )
703
704
 
704
705
 
706
+ @validation(
707
+ hook=ValidationHook.XBRL_FINALLY,
708
+ disclosureSystems=[DISCLOSURE_SYSTEM_EDINET],
709
+ )
710
+ def rule_gfm_1_3_21(
711
+ pluginData: PluginValidationDataExtension,
712
+ val: ValidateXbrl,
713
+ *args: Any,
714
+ **kwargs: Any,
715
+ ) -> Iterable[Validation]:
716
+ """
717
+ EDINET.EC5700W: [GFM 1.3.21] Remove the tuple definition.
718
+ """
719
+ tupleConcepts = [
720
+ concept for concept in pluginData.getExtensionConcepts(val.modelXbrl)
721
+ if concept.isTuple
722
+ ]
723
+ if len(tupleConcepts) > 0:
724
+ yield Validation.warning(
725
+ codes='EDINET.EC5700W.GFM.1.3.21',
726
+ msg=_("Remove the tuple definition."),
727
+ modelObject=tupleConcepts
728
+ )
729
+
730
+
731
+ @validation(
732
+ hook=ValidationHook.XBRL_FINALLY,
733
+ disclosureSystems=[DISCLOSURE_SYSTEM_EDINET],
734
+ )
735
+ def rule_gfm_1_3_22(
736
+ pluginData: PluginValidationDataExtension,
737
+ val: ValidateXbrl,
738
+ *args: Any,
739
+ **kwargs: Any,
740
+ ) -> Iterable[Validation]:
741
+ """
742
+ EDINET.EC5700W: [GFM 1.3.22] Do not set the xbrldt:typedDomainRef attribute on elements defined in submitter-specific taxonomies.
743
+ """
744
+ typedDomainConcepts = [
745
+ concept for concept in pluginData.getExtensionConcepts(val.modelXbrl)
746
+ if concept.isTypedDimension
747
+ ]
748
+
749
+ if len(typedDomainConcepts) > 0:
750
+ yield Validation.warning(
751
+ codes='EDINET.EC5700W.GFM.1.3.22',
752
+ msg=_("Do not set the xbrldt:typedDomainRef attribute on elements defined in submitter-specific taxonomies."),
753
+ modelObject=typedDomainConcepts
754
+ )
755
+
756
+
705
757
  @validation(
706
758
  hook=ValidationHook.XBRL_FINALLY,
707
759
  disclosureSystems=[DISCLOSURE_SYSTEM_EDINET],
@@ -886,3 +938,35 @@ def rule_gfm_1_5_8(
886
938
  label=label.textValue,
887
939
  modelObject=label
888
940
  )
941
+
942
+
943
+ @validation(
944
+ hook=ValidationHook.XBRL_FINALLY,
945
+ disclosureSystems=[DISCLOSURE_SYSTEM_EDINET],
946
+ )
947
+ def rule_gfm_1_5_10(
948
+ pluginData: PluginValidationDataExtension,
949
+ val: ValidateXbrl,
950
+ *args: Any,
951
+ **kwargs: Any,
952
+ ) -> Iterable[Validation]:
953
+ """
954
+ EDINET.EC5700W: [GFM 1.5.10] A non-numeric concept should not have a label with a numeric role
955
+ """
956
+ labelRelationshipSet = val.modelXbrl.relationshipSet(XbrlConst.conceptLabel)
957
+ if labelRelationshipSet is None:
958
+ return
959
+ for concept in val.modelXbrl.qnameConcepts.values():
960
+ if concept.isNumeric:
961
+ continue
962
+ labelRels = labelRelationshipSet.fromModelObject(concept)
963
+ for rel in labelRels:
964
+ label = rel.toModelObject
965
+ if not pluginData.isStandardTaxonomyUrl(label.modelDocument.uri, val.modelXbrl) and label.role in NUMERIC_LABEL_ROLES:
966
+ yield Validation.warning(
967
+ codes='EDINET.EC5700W.GFM.1.5.10',
968
+ msg=_("The non-numeric concept of '%(concept)s' has a label with a numeric role of '%(labelrole)s'"),
969
+ concept=concept.qname,
970
+ labelrole=label.role,
971
+ modelObject=label
972
+ )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: arelle-release
3
- Version: 2.37.57
3
+ Version: 2.37.58
4
4
  Summary: An open source XBRL platform.
5
5
  Author-email: "arelle.org" <support@arelle.org>
6
6
  License-Expression: Apache-2.0
@@ -1,6 +1,6 @@
1
1
  arelle/Aspect.py,sha256=Pn9I91D1os1RTVj6htuxTfRzVMhmVDtrbKvV_zy9xMI,5470
2
2
  arelle/BetaFeatures.py,sha256=T_tPac-FiozHyYLCemt0RoHJ1JahUE71L-0tHmIRKpE,858
3
- arelle/Cntlr.py,sha256=nwE_qIQMTa5R3sr_dLP6s3srLhiEA5zDOqfas2pDxoA,31691
3
+ arelle/Cntlr.py,sha256=HQ9JfpByqRf-C-ub07ALWMWUO2LIFev8tcmqpXjHW9s,31718
4
4
  arelle/CntlrCmdLine.py,sha256=pOztA3ExK4O-IvxELeBU0SyC4Yk6OKgbsOjpNgwmKiU,88800
5
5
  arelle/CntlrComServer.py,sha256=h1KPf31uMbErpxTZn_iklDqUMGFgQnjZkFkFjd8gtLQ,1888
6
6
  arelle/CntlrProfiler.py,sha256=2VQJudiUhxryVypxjODx2ccP1-n60icTiWs5lSEokhQ,972
@@ -16,12 +16,12 @@ arelle/DialogLanguage.py,sha256=HEBd21CfcJwGAGEu2gMyYHM0cXa0VVGAh-kxlZo8H9Q,7228
16
16
  arelle/DialogNewFactItem.py,sha256=blcE420dyMs6GEJW-YbtPj0lwc-YTUwyyEJh_Zvq9yE,8281
17
17
  arelle/DialogOpenArchive.py,sha256=8io1L6JhBVM8dLf23k2q7LsHCO6rpuOvknDmA3H5GSk,24004
18
18
  arelle/DialogPackageManager.py,sha256=YyWhHNFZT-ioM5jVbTJyHoQQK6_rB83j3DzMMswQGZk,30540
19
- arelle/DialogPluginManager.py,sha256=cr7GWNuYttg7H0pM5vaCPuxgDVgtZzR6ZamX957_1w8,32952
19
+ arelle/DialogPluginManager.py,sha256=0Uiy2Dei_49K7jbNnEUTchqcjANPIKNYNIt29iQoV1g,33035
20
20
  arelle/DialogRssWatch.py,sha256=mjc4pqyFDISY4tQtME0uSRQ3NlcWnNsOsMu9Zj8tTd0,13789
21
21
  arelle/DialogURL.py,sha256=JH88OPFf588E8RW90uMaieok7A_4kOAURQ8kHWVhnao,4354
22
22
  arelle/DialogUserPassword.py,sha256=kWPlCCihhwvsykDjanME9qBDtv6cxZlsrJyoMqiRep4,13769
23
23
  arelle/DisclosureSystem.py,sha256=mQlz8eezPpJuG6gHBV-x4-5Hne3LVh8TQf-Qm9jiFxI,24757
24
- arelle/ErrorManager.py,sha256=F81oi1syHgCFZ99L4e4j7bePGei5oJy34sIYizXd0OE,16762
24
+ arelle/ErrorManager.py,sha256=5BrpD9Ej8b-2JFR-GgWL32Oc2qItYnLbZApaToDKt0o,16972
25
25
  arelle/FileSource.py,sha256=asaX2wM47T7S6kELwmXm-YjGIoV6poWz_YdYThY0lpk,47983
26
26
  arelle/FunctionCustom.py,sha256=d1FsBG14eykvpLpgaXpN8IdxnlG54dfGcsXPYfdpA9Q,5880
27
27
  arelle/FunctionFn.py,sha256=BcZKah1rpfquSVPwjvknM1pgFXOnK4Hr1e_ArG_mcJY,38058
@@ -53,7 +53,7 @@ arelle/ModelVersObject.py,sha256=cPD1IzhkCfuV1eMgVFWes88DH_6WkUj5kj7sgGF2M0I,260
53
53
  arelle/ModelVersReport.py,sha256=bXEA9K3qkH57aABn5l-m3CTY0FAcF1yX6O4fo-URjl8,73326
54
54
  arelle/ModelXbrl.py,sha256=oEEP-whMGZjG45BXeXabXcD_IzBp7yA-21gW6zlGe2U,61120
55
55
  arelle/PackageManager.py,sha256=BvPExMcxh8rHMxogOag-PGbX6vXdhCiXAHcDLA6Ypsc,32592
56
- arelle/PluginManager.py,sha256=Gnh7xmvyIQX2PyCwjDMFZVanCM9KW09I-x5x8YfDpJs,42220
56
+ arelle/PluginManager.py,sha256=axNOsdpUvhths9ePeTAF3hurwrOooXA-7cGnG43z99k,42676
57
57
  arelle/PluginUtils.py,sha256=0vFQ29wVVpU0cTY3YOBL6FhNQhhCTwShBH4qTJGLnvc,2426
58
58
  arelle/PrototypeDtsObject.py,sha256=0lf60VcXR_isx57hBPrc7vEMkFpYkVuK4JVBSmopzkQ,7989
59
59
  arelle/PrototypeInstanceObject.py,sha256=CXUoDllhDqpMvSQjqIYi1Ywp-J8fLhQRV9wVD2YXgVo,13204
@@ -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=CcYtsRRssUOvQGQ3FxPpMP9UMr7c8qKJ7JmvTpB0H5U,708
128
+ arelle/_version.py,sha256=DMtGvhO601kTygJwWMKYB_n05a0AM-s0DoCNqsyf8lE,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
@@ -312,14 +312,14 @@ 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=2RxNrXqmAqsh7nM9ZNZn6Uo0mTrfFJcb_E_HJw_MLcE,1516
315
+ arelle/plugin/validate/EDINET/Constants.py,sha256=6LVzBTt4045SxUjeQa37tlN8zYLF1QIz16khYo3eY4A,2418
316
316
  arelle/plugin/validate/EDINET/ControllerPluginData.py,sha256=1WhiS0RdrxeXz4pGDzWATEPqCopOh2spr8Z6Qra_Psk,8420
317
317
  arelle/plugin/validate/EDINET/CoverPageRequirements.py,sha256=ZR8pk1CQfUIi15as1zVF27W0kRlUF1M_Ygw7appDUno,4488
318
318
  arelle/plugin/validate/EDINET/DisclosureSystems.py,sha256=3rKG42Eg-17Xx_KXU_V5yHW6I3LTwQunvf4a44C9k_4,36
319
319
  arelle/plugin/validate/EDINET/FilingFormat.py,sha256=SFZ22zFk6RVIA9dpx3iVLlf2heKfZZqt2ZUXUje4BII,18789
320
320
  arelle/plugin/validate/EDINET/FormType.py,sha256=jFqjJACJJ4HhkY1t6Fqei0z6rgvH3Mp-dP04KwQVv3Q,2517
321
321
  arelle/plugin/validate/EDINET/ManifestInstance.py,sha256=o6BGlaQHSsn6D0VKH4zn59UscKnjTKlo99kSGfGdYlU,6910
322
- arelle/plugin/validate/EDINET/PluginValidationDataExtension.py,sha256=wR18Oregy8SBBH2G_u4zqP2_M0EBopx9_AxOZpd1Bao,21358
322
+ arelle/plugin/validate/EDINET/PluginValidationDataExtension.py,sha256=9YXy4f1EIYq4NGWaOP7dXjVStdtH4Mb4D4wACrIa2PU,22540
323
323
  arelle/plugin/validate/EDINET/ReportFolderType.py,sha256=Q-9a-5tJfhK-cjY8WUB2AT1NI-Nn9cFtARVOIJoLRGU,2979
324
324
  arelle/plugin/validate/EDINET/Statement.py,sha256=0Mw5IB7LMtvUZ-2xKZfxmq67xF_dCgJo3eNLweLFRHU,9350
325
325
  arelle/plugin/validate/EDINET/UploadContents.py,sha256=o29mDoX48M3S2jQqrJO4ZaulltAPt4vD-qdsWTMCUPc,1196
@@ -332,7 +332,7 @@ arelle/plugin/validate/EDINET/rules/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQ
332
332
  arelle/plugin/validate/EDINET/rules/contexts.py,sha256=KPoyWfRaURvxoGVcWP64mTMTAKPMSmQSX06RClCLddw,7590
333
333
  arelle/plugin/validate/EDINET/rules/edinet.py,sha256=g3IPMV5-mWbVwVmEwv5-h1LOtdfeBQ9gZI27m744FFw,12628
334
334
  arelle/plugin/validate/EDINET/rules/frta.py,sha256=N0YglHYZuLD2IuwE26viR2ViwUYjneBuMFU9vlrS0aQ,7616
335
- arelle/plugin/validate/EDINET/rules/gfm.py,sha256=8m6TkwIKVouENOZ4AmfBd3t_R_oODhEwbYzcsOZfyp4,33878
335
+ arelle/plugin/validate/EDINET/rules/gfm.py,sha256=-NHyo6QAkKoekAc5nLSpvSwQ0lImttoSD-_y1puK-y4,36741
336
336
  arelle/plugin/validate/EDINET/rules/manifests.py,sha256=MoT9R_a4BzuYdQVbF7RC5wz134Ve68svSdJ3NlpO_AU,4026
337
337
  arelle/plugin/validate/EDINET/rules/upload.py,sha256=E_sEhsuUkoH648PGWwpN90uOQMjKNFaZ8priucmYMOk,47908
338
338
  arelle/plugin/validate/ESEF/Const.py,sha256=JujF_XV-_TNsxjGbF-8SQS4OOZIcJ8zhCMnr-C1O5Ho,22660
@@ -684,9 +684,9 @@ arelle/utils/validate/ValidationUtil.py,sha256=9vmSvShn-EdQy56dfesyV8JjSRVPj7txr
684
684
  arelle/utils/validate/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
685
685
  arelle/webserver/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
686
686
  arelle/webserver/bottle.py,sha256=P-JECd9MCTNcxCnKoDUvGcoi03ezYVOgoWgv2_uH-6M,362
687
- arelle_release-2.37.57.dist-info/licenses/LICENSE.md,sha256=Q0tn6q0VUbr-NM8916513NCIG8MNzo24Ev-sxMUBRZc,3959
688
- arelle_release-2.37.57.dist-info/METADATA,sha256=2zuOGGta03sWvoswvqhJf2mZeNCt1kZ5M90_ZBKN6g8,9327
689
- arelle_release-2.37.57.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
690
- arelle_release-2.37.57.dist-info/entry_points.txt,sha256=Uj5niwfwVsx3vaQ3fYj8hrZ1xpfCJyTUA09tYKWbzpo,111
691
- arelle_release-2.37.57.dist-info/top_level.txt,sha256=fwU7SYawL4_r-sUMRg7r1nYVGjFMSDvRWx8VGAXEw7w,7
692
- arelle_release-2.37.57.dist-info/RECORD,,
687
+ arelle_release-2.37.58.dist-info/licenses/LICENSE.md,sha256=Q0tn6q0VUbr-NM8916513NCIG8MNzo24Ev-sxMUBRZc,3959
688
+ arelle_release-2.37.58.dist-info/METADATA,sha256=R7D4feFh8MfRYsXoyMrffu1IDXMxju2PLquC1Ht1H-0,9327
689
+ arelle_release-2.37.58.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
690
+ arelle_release-2.37.58.dist-info/entry_points.txt,sha256=Uj5niwfwVsx3vaQ3fYj8hrZ1xpfCJyTUA09tYKWbzpo,111
691
+ arelle_release-2.37.58.dist-info/top_level.txt,sha256=fwU7SYawL4_r-sUMRg7r1nYVGjFMSDvRWx8VGAXEw7w,7
692
+ arelle_release-2.37.58.dist-info/RECORD,,